/*
 * Copyright (c) 2011-2012 Alexander Dubu
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * o  Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * o  Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * o  Neither the name Axil nor the names of its contributors may be used to
 *    endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package axil.vm.opcode;

import axil.api.AxilObject;
import axil.api.extend.Environment;
import axil.api.extend.Parameters;
import axil.api.extend.Source;
import axil.etc.NameValue;
import axil.framework.error.CompilationException;
import axil.framework.error.EvaluationException;
import axil.framework.localization.Message;
import axil.framework.localization.MessageContainer;
import axil.framework.type.SpecialValue;
import axil.vm.stack.StackFrame;

import static axil.framework.Functions.axil;


/**
 * A low-level instruction for the Axil virtual machine. You really don't want
 * to be messing around in here if you are trying to extend Axil capabilities.
 */
public abstract class Opcode extends SpecialValue implements Source, Relinkable {
	private int source;
	private int line;


	private static final Source none = new Source() {
		public int source()	{
			return 0;
		}
		public int line()	{
			return 0;
		}
	};


	/**
	 * This constructor is used only when creating anonymous instances suitable
	 * for loading the function definition. Use this with caution! This is not
	 * meant to create an executable object.
	 */
	protected Opcode() {
		this(none);
	}


	protected Opcode(Source source) {
		this.source = source.source();
		this.line = source.line();
	}


	/**
	 * Get the unique ID assigned to the source element, such as the module,
	 * function or script.  If the object lives outside of source code, then
	 * zero is returned.
	 */
	public int source() {
		return source;
	}


	/**
	 * Get the line number in the original source where this object resides.
	 * If the object exists outside of source code (such as the boolean keyword
	 * "true"), then zero is returned.
	 */
	public int line() {
		return line;
	}


	/**
	 * Tell if new instances of this type can be created by a script. All Axil
	 * types in the standard library can be created if that module in which
	 * they appear is visible to a script. However, the host application will
	 * typically want to prevent creation of new host application objects.
	 *
	 * @return
	 * 	Returns true if a script can create instances of this type. Even if
	 * 	false is returned, Java code may still create instances of this type.
	 */
	public boolean creatable() {
		return false;
	}


	/**
	 * Evaluate this object in the given context and environment. A non-null
	 * object must be returned.
	 */
	protected abstract AxilObject evaluate(Environment env, Parameters params);


	/**
	 * Dump the content of this node to the diagnostic device. This is strictly
	 * for internal diagnostics, not for application use.
	 */
	public abstract void dump(int indent);


	protected CompilationException compile(Source source, String key,
	                                       NameValue... values) {
		return new CompilationException(source, key, values);
	}


	protected EvaluationException evaluation(Parameters params, String key,
	        NameValue... values) {
		return new EvaluationException((StackFrame)params,
		                               new Message(axil(), key, values));
	}


	/**
	 * Evaluate this object in the given context and environment.
	 *
	 * @param env
	 * 	The environment in which the function is being invoked. The environment
	 * 	is never null.
	 *
	 * @param params
	 * 	The parameters to the function, which can be any Axil object. The values
	 * 	given are never null but may be the nil object (Axil.NIL).
	 */
	public AxilObject eval(Environment env, Parameters params) {
		try {
			return evaluate(env, params);
		} catch (Throwable e) {
			if (e instanceof MessageContainer) {
				throw new EvaluationException(e, (StackFrame)params,
				                              ((MessageContainer)e).message());
			}
			throw new EvaluationException(e, (StackFrame)params,
			                              new Message(axil(), "generic-runtime-error"));
		}
	}

	/**
	 * Get the unique identity for this object. The value returned is guaranteed
	 * to be a valid Axil identifier.
	 */
	public String identity() {
		return "(opcode + " + this.getClass().getSimpleName() + ')';
	}


	/**
	 * Return a string representation of this object. The string returned is
	 * never null.
	 */
	public String toString() {
		return "(opcode)";
	}
}
